﻿//------------------------------------------------------------------------------
// File: BasicRender.fx
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------------------------


//------------------------------------------------------------------------------
// Global variables
//------------------------------------------------------------------------------
float4 g_MaterialAmbientColor;      // Material's ambient color
float4 g_MaterialDiffuseColor;      // Material's diffuse color

float3 g_LightPos;					// Position of light
float3 g_LightDir;					// Direction of light (temp)
float4x4 g_mLightView;				// View matrix of light
float4x4 g_mLightProj;				// Projection matrix of light

float4 g_LightDiffuse;				// Light's diffuse color
float4 g_LightAmbient;              // Light's ambient color

texture g_MeshTexture;              // Color texture for mesh
texture g_ShadowMapTexture;			// Shadow map texture for lighting

float4x4 g_mWorld;                  // World matrix for object
float3 g_CameraPos;				    // Camera position for scene View 
float4x4 g_mCameraView;				// Camera's view matrix
float4x4 g_mCameraProj;				// Projection matrix


//------------------------------------------------------------------------------
// Texture samplers
//------------------------------------------------------------------------------
sampler MeshTextureSampler = 
sampler_state
{
    Texture = <g_MeshTexture>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
};
sampler ShadowMapSampler =
sampler_state
{
	Texture = <g_ShadowMapTexture>;
    MinFilter = POINT;
    MagFilter = POINT;
    MipFilter = POINT;
    AddressU = Clamp;
    AddressV = Clamp;
};

float4 BoxSampling(sampler Sampler, float2 UV, float2 textureSize) 
{ 
	float2 texelpos = textureSize * UV;  
	float2 lerps = frac(texelpos);  
	float texelSize = 1.0 / textureSize;                  
  
	float4 sourcevals[4];  
	sourcevals[0] = tex2D(Sampler, UV);  
	sourcevals[1] = tex2D(Sampler, UV + float2(texelSize, 0));  
	sourcevals[2] = tex2D(Sampler, UV + float2(0, texelSize));  
	sourcevals[3] = tex2D(Sampler, UV + float2(texelSize, texelSize));    
          
	float4 interpolated = lerp(lerp(sourcevals[0], sourcevals[1], lerps.x), 
		lerp(sourcevals[2], sourcevals[3], lerps.x ), lerps.y);  
 
	return interpolated; 
} 
 
//------------------------------------------------------------------------------
// Vertex shader output structure
//------------------------------------------------------------------------------
struct VS_OUTPUT
{
    float4 Position   : POSITION0;   // vertex position 
    float2 TextureUV  : TEXCOORD0;  // vertex texture coords   
    float3 vNormal	  : TEXCOORD1;
    float4 vPos       : TEXCOORD2;  
};
struct PS_INPUT
{
    float2 TextureUV  : TEXCOORD0;  // vertex texture coords   
    float3 vNormal	  : TEXCOORD1;
    float4 vPos       : TEXCOORD2;  
};
struct VS_SHADOW_OUTPUT
{
	float4 Position : POSITION;
	float Depth : TEXCOORD0;
	float4 wpos : TEXCOORD1;
};



//------------------------------------------------------------------------------
// Utility function(s)
//------------------------------------------------------------------------------
float4x4 CreateLookAt(float3 cameraPos, float3 target, float3 up)
{
	float3 zaxis = normalize(cameraPos - target);
	float3 xaxis = normalize(cross(up, zaxis));
	float3 yaxis = cross(zaxis, xaxis);
	
	float4x4 view = { xaxis.x, yaxis.x, zaxis.x, 0,
		xaxis.y, yaxis.y, zaxis.y, 0,
		xaxis.z, yaxis.z, zaxis.z, 0,
		-dot(xaxis, cameraPos), -dot(yaxis, cameraPos),
		-dot(zaxis, cameraPos), 1
	};

	return view;
}
float4 GetPositionFromLight(float4 position)
{
	float4x4 WorldViewProjection =
	 mul(mul(g_mWorld, g_mLightView), g_mLightProj);
	return mul(position, WorldViewProjection);  
}
//------------------------------------------------------------------------------
// This shader computes rudimentary transform and lighting.
// The XNA VertexDeclaration of our models is PositionNormalTexture.
//------------------------------------------------------------------------------

VS_OUTPUT RenderShadowsVS(
     float3 position : POSITION,
     float3 normal : NORMAL,
     float2 vTexCoord0 : TEXCOORD0 )
{
     VS_OUTPUT Output;

     //generate the world-view-projection matrix
     float4x4 wvp = mul(mul(g_mWorld, g_mCameraView), g_mCameraProj);
     
     //transform the input position to the output
     Output.Position = mul(float4(position, 1.0), wvp);

	 //transform the normal to world space : unless g_mWorld is non-uniform scale, just multiplying world matrix is ok.
     Output.vNormal =  mul(normal, g_mWorld);
     
     //do not transform the position needed for the
     //shadow map determination
     Output.vPos = float4(position,1.0);
     
     //pass the texture coordinate as-is
	 Output.TextureUV = vTexCoord0;
    
     //return the output structure
     return Output;
}


//------------------------------------------------------------------------------
// Pixel shader output structure
//------------------------------------------------------------------------------
struct PS_OUTPUT
{
    float4 RGBColor : COLOR0;  // Pixel color    
};


//------------------------------------------------------------------------------
// This shader outputs the pixel's color by modulating the texture's
//       color with diffuse material color
//------------------------------------------------------------------------------

PS_OUTPUT RenderShadowsPS( PS_INPUT In ) 
{ 
    PS_OUTPUT Output;
    
    // Standard lighting equation
    float4 vTotalLightDiffuse = float4(0,0,0,1);
    float3 lightDir = normalize(g_LightPos-In.vPos);  // direction of light
    vTotalLightDiffuse += g_LightDiffuse * max(0,dot(In.vNormal, lightDir)); 
    vTotalLightDiffuse.a = 1.0f;

	// Now, consult the ShadowMap to see if we're in shadow
    float4 lightingPosition = GetPositionFromLight(In.vPos);// Get our position on the shadow map
   
    // Get the shadow map depth value for this pixel   
    float2 ShadowTexC = 0.5 * lightingPosition.xy / lightingPosition.w + float2( 0.5, 0.5 );
	// uv좌표니 Y축은 뒤집자.
    ShadowTexC.y = 1.0f - ShadowTexC.y;

	// shadow depth를 얻는다.
    //float shadowdepth = tex2D(ShadowMapSampler, ShadowTexC).r;    
	float shadowdepth = BoxSampling(ShadowMapSampler, ShadowTexC, 1024).x; 

    // Check our value against the depth value
    //float ourdepth = 1- (lightingPosition.z / lightingPosition.w);
    float ourdepth = (lightingPosition.z / lightingPosition.w);

	float shadow_factor = 1.f;
	
	//if (ourdepth > shadowdepth)
	//	shadow_factor = 0.f;
		
	float far = 100;
//	shadow_factor = log(exp( -(far * (ourdepth - shadowdepth) ))) + 1.f;
	shadow_factor = exp( -(far * (ourdepth - shadowdepth) ));

//	if (shadow_factor > 0.98f)
//		shadow_factor = 1.f;
//	else
//		shadow_factor = - log( shadow_factor );
//		shadow_factor = 0.f;
    
    Output.RGBColor = shadow_factor; // * tex2D(MeshTextureSampler, In.TextureUV); 
	//Output.RGBColor = tex2D(MeshTextureSampler, In.TextureUV) * (vTotalLightDiffuse + g_LightAmbient);
        
    return Output;
    
}
PS_OUTPUT DiffuseOnlyPS(VS_OUTPUT In) : COLOR
{
	 PS_OUTPUT Output;
     //calculate per-pixel diffuse
     float3 directionToLight = normalize(g_LightPos - In.vPos);
     float diffuseIntensity = saturate( dot(directionToLight, In.vNormal));
     float4 diffuse = g_LightDiffuse * diffuseIntensity;
     
     float4 color = diffuse + g_LightAmbient;
     color.a = 1.0;
     
     Output.RGBColor = tex2D(MeshTextureSampler, In.TextureUV) * color;
     
     return Output;
}

PS_OUTPUT TextureOnlyPS(float2 TextureUV  : TEXCOORD0) : COLOR
{
     PS_OUTPUT Output;
     Output.RGBColor = tex2D(MeshTextureSampler, TextureUV);
	 Output.RGBColor.a = 1.0;
     
     return Output;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
// for Shadowmap rendering
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

VS_SHADOW_OUTPUT RenderShadowMapVS(float4 vPos: POSITION)
{
	VS_SHADOW_OUTPUT Out;

	Out.Position = GetPositionFromLight(vPos);			// light space의 view-projection 공간으로 transform
	Out.Depth.x = (Out.Position.z/Out.Position.w);		// 그냥 호환성 유지

	Out.wpos = mul( vPos, g_mWorld );					// 월드만 곱해둔다.

	////Out.Position = GetPositionFromLight(vPos); 
	////// Depth is Z/W.  This is returned by the pixel shader.
	////// Subtracting from 1 gives us more precision in floating point.
	////Out.Depth.x = 1-(Out.Position.z/Out.Position.w);	
	return Out;
}

float4 RenderShadowMapPS( VS_SHADOW_OUTPUT In ) : COLOR
{
	// The depth is Z divided by W. We return
	// this value entirely in a 32-bit red channel
	// using SurfaceFormat.Single.  This preserves the
	// floating-point data for finer detail.
	return float4(In.Depth.x,0,0,1);
}


//------------------------------------------------------------------------------
// Renders scene to render target
//------------------------------------------------------------------------------
technique TextureRender
{
	pass P0
	{
        VertexShader = compile vs_3_0 RenderShadowsVS();
        PixelShader  = compile ps_3_0 TextureOnlyPS(); 
	}
}
technique ShadowRender
{
	pass P0
	{
        VertexShader = compile vs_3_0 RenderShadowsVS();
        PixelShader  = compile ps_3_0 RenderShadowsPS(); 
	}
}
technique ShadowMapRender
{
	pass P0
	{
		// These render states are necessary to get a shadow map.
		// You should consider resetting CullMode and AlphaBlendEnable
		// before you render your main scene.	
		CullMode = NONE;
		ZEnable = TRUE;
		ZWriteEnable = TRUE;
		AlphaBlendEnable = FALSE;
		
        VertexShader = compile vs_3_0 RenderShadowMapVS();
        PixelShader  = compile ps_3_0 RenderShadowMapPS();
	
	}
}
